/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Element;
import org.jdom2.Namespace;

import com.netscenery.util.ErrTag;
import com.netscenery.util.QName;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class YangUnknown extends ModelElement {
	private String keyword;
	private String prefix;
	private String extensionName;
	private ArrayList<ModelElement> subElements;

	public YangUnknown(Env env,String keyword){
		this.setEnv(env);
		this.setKeyword(keyword);
	}
	
	public YangUnknown(Env env,String keyword,String name){
		this.setEnv(env);
		this.setKeyword(keyword);
		if (null != name){
			this.setName(name);
		}
	}
	public void setKeyword(String keyword){
		if (!ModelElement.isExtensionKeyword(env, keyword)){
			return;
		}
		
		this.keyword = keyword;
		QName qname = new QName(keyword);
		this.setPrefix(qname.getPrefix());
		this.setExtensionName(qname.getLocalName());
		return;
	}
	
	public boolean checkKeyword(String keyword){
		
		if (null == keyword){
			return false;
		}
		if (!keyword.contains(":")){
			return false;
		}
		
		String[] strs = keyword.split(":");
		if (2 != strs.length){
			return false;
		}
		String prefix = strs[0];
		String identifier = strs[1];
		if (!env.hasPrefix(prefix)){
			return false;
		}
		
		if (!isIdentifier(identifier)){
			return false;
		}
		Module m = env.getCurModule().getModule(prefix);
		if (null == m){
			return false;
		}
		
		//�Ȳ�һ�±�ģ���extension�б�����û��
		Extension extension = m.getExtension(identifier);
		if (null != extension){
			return true;
		}
		//���û�ҵ�������Ҫ����ģ������ģ���в���
		int includenum = m.getIncludesNumber();
		if (0 == includenum){
			return false;
		}
		for (int i = 0; i < includenum; i++){
			Include in = m.getInclude(i);
			if (null == in ){
				continue;
			}
			
			SubModule sm = in.getInclude();
			if (null != sm){
				if (null != sm.getExtension(identifier)){
					return true;
				}
			}
		}
		return false;
		
	}
	public boolean checkName(String name){
		return true;
		
	}
	
	public String getKeyword(){
		return this.keyword;
	}
	/**
	 * @return the prefix
	 */
	public String getPrefix() {
		return prefix;
	}

	/**
	 * @param prefix the prefix to set
	 */
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	/**
	 * @return the extensionName
	 */
	public String getExtensionName() {
		return extensionName;
	}

	/**
	 * @param extensionName the extensionName to set
	 */
	public void setExtensionName(String extensionName) {
		this.extensionName = extensionName;
	}

	/**
	 * @return the subElements
	 */
	public ArrayList<ModelElement> getSubElements() {
		return subElements;
	}

	/**
	 * @param subElements the subElements to set
	 */
	public void setSubElements(ArrayList<ModelElement> subElements) {
		this.subElements = subElements;
	}
	
	public int getSubElementsNumber(){
		if(null == subElements){
			return 0;
		}
		
		return subElements.size();
	}
	public ModelElement getSubElement(int index){
		if (null == name){
			return null;
		}
		
		if (null == this.subElements){
			return null;
		}
		
		int size = subElements.size();
		if (index >= size){
			return null;
		}
		
		return subElements.get(index);
	}
	public boolean hasSubElement(ModelElement m){
		if (null == m){
			return false;
		}
		
		if (null == this.subElements){
			return false;
		}
		
		int size = subElements.size();
		for (int i = 0; i < size; i++){
			ModelElement n = subElements.get(i);
			if (null != n){
				if (n.equals(m)){
					return true;
				}
			}
		}
		
		return false;
	}
	public boolean addSubElement(ModelElement m){
		
		if (null == m){
			return false;
		}
		
		
		if (hasSubElement(m)){
			return false;
		}
		if (null == this.subElements){
			subElements = new ArrayList<ModelElement>(1);
		}
		this.subElements.add(m);
		return true;
	}
	
	public ModelElement removeSubElement(ModelElement m){
		if (null == m){
			return null;
		}
		if (null == subElements){
			return null;
		}
		
		int size = subElements.size();
		
		for (int i = 0; i < size; i++){
			ModelElement n = subElements.get(i);
			if (null != n){
				if (n.equals(m)){
					return subElements.remove(i);
				}
			}
		}
		
		return null;
	}
	

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		if (!checkKeyword(this.keyword)){
			validator.setIsValid(false);
			validator.addMsg(new Message(this,ErrTag.ERROR,"invalid keyword"));
		}
		int size = getSubElementsNumber();
		for (int i = 0; i < size; i++){
			ModelElement m = getSubElement(i);
			if (null == m){
				continue;
			}
			Validator v = m.validate();
			validator.copy(v);
		}
		
		return validator;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {

		
		YangStatement statement = new YangStatement(this.keyword,this.name);
		if (null != this.getSubElements()){
			int size = this.getSubElements().size();
			for (int i = 0; i < size ; i++){
				ModelElement m = this.getSubElement(i);
				if (null == m){
					continue;
				}
				YangStatement child = m.toYang();
				if (null != child){
					statement.addChild(child);
				}
			}
		}
		
		return statement;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {	
		MainModule mm = (MainModule)(env.getCurModule().getModule(this.prefix));
		if (null == mm){
			return null;
		}
		
		Namespace nm = Namespace.getNamespace(this.prefix, mm.getNamespace());
		Element element = new Element(this.extensionName);
		element.addNamespaceDeclaration(nm);
		element.setNamespace(nm);
		if (null != this.name){
			Element name = new Element(XMLTag.XML_TAG_NAME);
			name.setText(this.name);
			element.addContent(name);
		}
		if (null != this.getSubElements()){
			int size = this.getSubElements().size();
			for (int i = 0; i < size ; i++){
				ModelElement m = this.getSubElement(i);
				if (null == m){
					continue;
				}
				Element child = m.toXML();
				if (null != child){
					element.addContent(child);
				}
			}
		}
		
		return element;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		List<Element> children = element.getChildren();
		if (null == children){
			return MODELELEMENT_OK;
		}
		
		int childrenNumber = children.size();
		if (0 == childrenNumber){
			return MODELELEMENT_OK;
		}
		
		for (int i = 0; i < childrenNumber; i++){
			Element child = children.get(i);
			if (null == child){
				continue;
			}
			if (child.getName().equals(XMLTag.XML_TAG_NAME)){
				this.setName(child.getText()) ;
			}
			else {
				Env subEnv = this.newSubEnv();
				ModelElement m = null;
				try {
					 m = ModelElement.getInstance(subEnv, child);
				} catch (InstantiationException e) {
					throw new ModelException(ErrTag.ERROR,
							e.toString());
					
				} catch (IllegalAccessException e) {
					throw new ModelException(ErrTag.ERROR,
							e.toString());
				} catch (ModelException e) {
					// TODO Auto-generated catch block
					if (ErrTag.ERROR != e.getErrorTag()){
						System.out.println(e.toString());
						this.setErrorMsg(e.toString());
					}
					else {
						throw e; 
					}
				}
				
				if (null != m){
					if (MODELELEMENT_OK == m.readXML(child)){
						this.addSubElement(m);
					}
				}
			}

		}
		
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		// TODO Auto-generated method stub
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (null != statement.getArgument()){
			this.setName(statement.getArgument());
		}
		int childrenNumber = statement.getChildrenNumber();
		if (0 == childrenNumber){
			return MODELELEMENT_OK;
		}
		
		for (int i = 0; i < childrenNumber; i++){
			YangStatement childstat = statement.getChild(i);
			if (null == childstat){
				continue;
			}
			Env subEnv = this.newSubEnv();
			ModelElement m = null;
			try {
				 m = ModelElement.getInstance(subEnv, 
						childstat.getKeyword(), childstat.getArgument());
			} catch (InstantiationException e) {
				throw new YangParserException(childstat.getParserEnv(),ErrTag.ERROR,
						childstat.getParserEnv().getBeginLine(),
						childstat.getParserEnv().getBeginColumn(),
						e.toString());
				
			} catch (IllegalAccessException e) {
				throw new YangParserException(childstat.getParserEnv(),ErrTag.ERROR,
						childstat.getParserEnv().getBeginLine(),
						childstat.getParserEnv().getBeginColumn(),
						e.toString());
			} catch (ModelException e) {

				YangParserException ee =  new YangParserException(childstat.getParserEnv(),e.getErrorTag(),
						childstat.getParserEnv().getBeginLine(),
						childstat.getParserEnv().getBeginColumn(),
						e.getDescription());
				if (ErrTag.ERROR != ee.getErrorTag()){
					System.out.println(ee.toString());
					this.setErrorMsg(ee.toString());
				}
				else {
					throw ee; 
				}
			}
			
			if (null != m){
				if (MODELELEMENT_OK == m.readYANG(childstat)){
					this.addSubElement(m);
				}
			}
		}
		
		return MODELELEMENT_OK;
	}

	public  MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Element toYin() {
		MainModule mm = (MainModule)(env.getCurModule().getModule(this.prefix));
		if (null == mm){
			return null;
		}
		
		Namespace nm = Namespace.getNamespace(this.prefix, mm.getNamespace());
		Element element = new Element(this.extensionName);
		Extension ext = mm.getExtension(this.extensionName);
		if (null == ext){
			return null;
		}
		if (ext.isYinElement()){
			Element arg = new Element(ext.getArgument());
			arg.setText(this.name);
			element.addContent(arg);
		}
		else {
			Attribute attr = new Attribute(ext.getArgument(),this.name);
			element.setAttribute(attr);
			
		}
		
		element.addNamespaceDeclaration(nm);
		element.setNamespace(nm);
		
		if (null != this.getSubElements()){
			int size = this.getSubElements().size();
			for (int i = 0; i < size ; i++){
				ModelElement m = this.getSubElement(i);
				if (null == m){
					continue;
				}
				Element child = m.toYin();
				if (null != child){
					element.addContent(child);
				}
			}
		}
		
		return element;
	}

	@Override
	public int readYin(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		MainModule mm = (MainModule)(env.getCurModule().getModule(this.prefix));
		if (null == mm){
			return MODELELEMENT_FAIL;
		}
		
		Extension ext = mm.getExtension(this.extensionName);
		if (ext.isYinElement()){
			Element arg = element.getChild(ext.getArgument(),env.getNs());
			this.setName(arg.getText());
		}
		else {
			this.setName(element.getAttributeValue(ext.getArgument()));
		}
		List<Element> children = element.getChildren();
		if (null == children){
			return MODELELEMENT_OK;
		}
		
		int childrenNumber = children.size();
		if (0 == childrenNumber){
			return MODELELEMENT_OK;
		}
		int start = 0;
		if (ext.isYinElement()){
			start = 1;
		}
		for (int i = start; i < childrenNumber; i++){
			Element child = children.get(i);
			if (null == child){
				continue;
			}
			{
				Env subEnv = this.newSubEnv();
				ModelElement m = null;
				try {
					 m = ModelElement.getYinInstance(subEnv, child);
				} catch (InstantiationException e) {
					throw new ModelException(ErrTag.ERROR,
							e.toString());
					
				} catch (IllegalAccessException e) {
					throw new ModelException(ErrTag.ERROR,
							e.toString());
				} catch (ModelException e) {
					// TODO Auto-generated catch block
					if (ErrTag.ERROR != e.getErrorTag()){
						System.out.println(e.toString());
						this.setErrorMsg(e.toString());
					}
					else {
						throw e; 
					}
				}
				
				if (null != m){
					if (MODELELEMENT_OK == m.readYin(child)){
						this.addSubElement(m);
					}
				}
			}

		}
		
		return MODELELEMENT_OK;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}


}
